/*	Copyright 2013 RangerUFO
 *
 *	This file is part of Libevent-helper.
 *
 *	Libevent-helper is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU Lesser General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	Libevent-helper is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU Lesser General Public License for more details.
 *
 *	You should have received a copy of the GNU Lesser General Public License
 *	along with Libevent-helper.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <event2/helper.h>
#include <event2/acceptor.h>
#include <event2/session.h>
#include <stdio.h>
#include <string.h>

void read_cb(struct evsession* s, void* args)
{
	struct evbuffer* in = evsession_get_input(s);
	struct evtable_node* tn = (struct evtable_node*)args;
	struct evtable* tb = evtable_node_table(tn);

	for (char* line = evbuffer_readln(in, 0, EVBUFFER_EOL_CRLF); line; evhelper_free(line), line = evbuffer_readln(in, 0, EVBUFFER_EOL_CRLF))
	{
		if (strcmp(line, "__chat_server_shutdown") == 0)
		{
			event_base_loopexit(evsession_get_base(s), 0);
			evhelper_free(line);
			break;
		}

		for (struct evtable_node* i = evtable_get_first(tb); i; i = evtable_get_next(tb, i))
		{
			struct evbuffer* out = evsession_get_output(evtable_node_deref(i));
			evbuffer_add_printf(out, "%s:%d say: %s\r\n", evsession_get_peer_ip(s), evsession_get_peer_port(s), line);
		}
	}
}

void event_cb(struct evsession* s, short what, void* args)
{
	if (what & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
	{
		struct evtable_node* tn = (struct evtable_node*)args;
		struct evtable* tb = evtable_node_table(tn);
		
		printf("%s:%d disconnect.\n", evsession_get_peer_ip(s), evsession_get_peer_port(s));
		fflush(stdout);

		for (struct evtable_node* i = evtable_get_first(tb); i; i = evtable_get_next(tb, i))
		{
			struct evbuffer* buf = evsession_get_output(evtable_node_deref(i));
			evbuffer_add_printf(buf, "SYS: %s:%d disconnect.\r\n", evsession_get_peer_ip(s), evsession_get_peer_port(s));
		}

		evtable_remove(tb, tn);
	}
}

void accept_cb(struct evacceptor* ac, struct evsession* s, void* args)
{
	struct evtable* tb = (struct evtable*)args;
	struct evtable_node* tn = evtable_insert(tb, s);

	if (tn)
	{
		printf("%s:%d connect.\n", evsession_get_peer_ip(s), evsession_get_peer_port(s));
		fflush(stdout);
		evsession_setcb(s, read_cb, 0, event_cb, tn);

		for (struct evtable_node* i = evtable_get_first(tb); i; i = evtable_get_next(tb, i))
		{
			struct evbuffer* buf = evsession_get_output(evtable_node_deref(i));
			evbuffer_add_printf(buf, "SYS: %s:%d connect.\r\n", evsession_get_peer_ip(s), evsession_get_peer_port(s));
		}

		evbuffer_add_printf(evsession_get_output(s), "SYS: Welcom to the chat room!\r\n");
	}
	else
	{
		puts("Chat room is full.");
		fflush(stdout);
		evsession_free(s);
	}
}

void accept_error_cb(struct evacceptor* ac, void* args)
{
	puts("Error from accept() call.");
	fflush(stdout);
}

void session_close(struct evtable_node* tn)
{
	evsession_free((struct evsession*)evtable_node_deref(tn));
}

int main()
{
	if (evhelper_init() == -1)
	{
		puts("Init evhelper failed.");
		return -1;
	}

	struct event_base* base = event_base_new();

	if (!base)
	{
		puts("Create event_base failed.");
		return -1;
	}

	struct evacceptor* ac = evacceptor_new_bind(base, 0, 53000, -1);

	if (!ac)
	{
		puts("Create evacceptor failed.");
		event_base_free(base);
		return -1;
	}

	struct evtable* st = evtable_new(100, session_close);

	if (!st)
	{
		puts("Create session table failed.");
		evacceptor_free(ac);
		event_base_free(base);
		return -1;
	}

	evacceptor_setcb(ac, accept_cb, accept_error_cb, st);

	int ret = event_base_dispatch(base);

	evtable_free(st);
	evacceptor_free(ac);
	event_base_free(base);
	
	return ret;
}
